package com.dave.gd.core.services.utils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import com.dave.gd.common.exception.RtManagerException;
import com.dave.gd.common.util.StringUtils;
import com.dave.gd.common.util.spring.ApplicationContextUtil;

/**
 * .
 * 
 */
public class ServiceFactory {
    
    /**
     * .
     */
    private Map<String, String> factoryMap;
    
    /**
     * .
     */
    private static class SingletonHolder {
        private static ServiceFactory INSTANCE = new ServiceFactory();
    }
    
    /**
     * .
     */
    public static HashMap<String, Method> methodMap = new HashMap<String, Method>();
    
    /**
     * .
     * 
     */
    public ServiceFactory() {
        factoryMap = new HashMap<String, String>();
    }
    
    /**
     * .
     * 
     */
    public static ServiceFactory getDefaultFactory() {
        return SingletonHolder.INSTANCE;
    }
    
    /**
     * .
     * 
     */
    public void registerService(String name, Class<Object> serviceClass) {
        factoryMap.put(name, serviceClass.getName());
    }
    
    public void registerService(String name, String serviceClass) {
        factoryMap.put(name, serviceClass);
    }
    
    /**
     * .
     * 
     */
    public String getServiceClazzName(String serviceName) {
        String fac = (String) factoryMap.get(serviceName);
        return StringUtils.isBlank(fac) ? serviceName
            : fac;
    }
    
    /**
     * @param serviceName
     *            String
     * @return Object
     */
    public Object getServiceInstance(String serviceName) {
        String serviceClazzName = this.getServiceClazzName(serviceName);
        return this.getServiceInstanceByClazzName(serviceClazzName);
    }
    
    /**
     * String
     * 
     * @return Object
     */
    public Object getServiceInstanceByClazzName(String serviceClazzName) {
        try {
            Object service = null;
            if (serviceClazzName.indexOf("$") == 0) { // Spring
                String beanId = serviceClazzName.replace("$", "");
                return ApplicationContextUtil.getBean(beanId);
            } else { // Class.forName
                service = Class.forName(serviceClazzName).newInstance();
            }
            return service;
        } catch (InstantiationException e) {
            throw new RtManagerException("根据服务：" + serviceClazzName + "获取实例异常", e);
        } catch (IllegalAccessException e) {
            throw new RtManagerException("根据服务：" + serviceClazzName + "获取实例异常", e);
        } catch (ClassNotFoundException e) {
            throw new RtManagerException("根据服务：" + serviceClazzName + "获取实例异常", e);
        }
    }
    
    /**
     * @param clazzName
     *            String
     * @param methodName
     *            String
     * @param argsum
     *            int
     * @return Method
     */
    public Method getClazzMethod(String clazzName, String methodName, int argsum) {
        Object serviceInstance = this.getServiceInstanceByClazzName(clazzName);
        return this.getClazzMethod(serviceInstance, methodName, argsum);
    }
    
    /**
     * @param serviceInstance
     *            Object
     * @param methodName
     *            String
     * @param argsum
     *            int
     * @return Method
     */
    public Method getClazzMethod(Object serviceInstance, String methodName, int argsum) {
        String key = serviceInstance.getClass().getName() + ServConstants.SERV_FACTORY_TAG
            + methodName + ServConstants.SERV_FACTORY_TAG + argsum;
        if (!methodMap.containsKey(key)) {
            Method[] methodList = serviceInstance.getClass().getMethods();
            for (int i = 0; i < methodList.length; i++) {
                Method method = methodList[i];
                Class<?>[] param = method.getParameterTypes();
                String mangledName = serviceInstance.getClass().getName()
                    + ServConstants.SERV_FACTORY_TAG + method.getName()
                    + ServConstants.SERV_FACTORY_TAG + param.length;
                methodMap.put(mangledName, methodList[i]);
            }
        }
        return methodMap.get(key);
    }
    
    /**
     * @param clazzName
     *            String
     * @param methodName
     *            String int
     * @return Method
     */
    public Method getClazzMethod(String clazzName, String methodName, Object... args) {
        Object serviceInstance = this.getServiceInstanceByClazzName(clazzName);
        return this.getClazzMethod(serviceInstance, methodName, args);
    }
    
    /**
     * @param serviceInstance
     *            Object
     * @param methodName
     *            String
     * @return Method
     */
    public Method getClazzMethod(Object serviceInstance, String methodName, Object... args) {
        String key = serviceInstance.getClass().getName() + ServConstants.SERV_FACTORY_TAG
            + methodName + ServConstants.SERV_FACTORY_TAG + args.length;
        for (Object obj : args) {
            key += ServConstants.SERV_FACTORY_TAG + obj.getClass().getName();
        }
        if (!methodMap.containsKey(key)) {
            Method[] methodList = serviceInstance.getClass().getMethods();
            for (int i = 0; i < methodList.length; i++) {
                Method method = methodList[i];
                Class<?>[] param = method.getParameterTypes();
                if (param.length == args.length && methodName.equals(method.getName())) {
                    
                    boolean sample = true;
                    for (int j = 0; j < param.length; j++) {
                        if (param[j].getName().equals(args[j].getClass().getName())) { // 参数类型全相同
                            sample = sample && true;
                        } else if (param[j].isAssignableFrom(args[j].getClass())) { // 方式参数是入参的父类
                            sample = sample && true;
                        } else {
                            sample = false;
                            break;
                        }
                    }
                    if (sample) {
                        methodMap.put(key, methodList[i]);
                        break;
                    }
                }
            }
        }
        return methodMap.get(key);
    }
    
}
